
#include "JournalLoggerHandler.hpp"

#include <chrono>
#include <cstring>
#include <ctime>
#include <filesystem>

#include "DbusManager.hpp"
#include "LogCollection.hpp"

using LogCollector::Severity;
using LogCollector::Logger;
using LogCollector::ReturnStatus;

using LogCollector::TIME_CONVERSION_BASE;
using LogCollector::TIME_CONVERSION_DOUBLE;
using LogCollector::TIME_YEAR_OFFSET;
using LogCollector::TIME_MONTH_OFFSET;

namespace fs = std::filesystem;


JournalLoggerHandler::JournalLoggerHandler(
    const std::string& aServiceName, const std::string& aServicePath, const std::string& aServiceInterface)
: DbusContext(aServiceName, aServicePath, aServiceInterface)
{
}

JournalLoggerHandler::~JournalLoggerHandler()
{
    if (mJournal) {
        sd_journal_close(mJournal);
        mJournal = nullptr;
    }
}

bool JournalLoggerHandler::prepare()
{
    if (!registerJournalHandler()) {
        return false;
    }

    return true;
}

std::string JournalLoggerHandler::getJournalField(sd_journal *aJournal, const char *aField)
{
    const void *data;
    size_t len;
    if (sd_journal_get_data(aJournal, aField, &data, &len) == 0) {
        const char* raw = static_cast<const char*>(data);
        return std::string(raw + strlen(aField) + 1, len - strlen(aField) - 1);
    }

    return "";
}

std::string JournalLoggerHandler::convertRawTime(uint64_t aRawTime)
{
    using namespace std::chrono;
    
    auto sec = seconds(aRawTime / TIME_CONVERSION_DOUBLE);
    auto msec = milliseconds((aRawTime % TIME_CONVERSION_DOUBLE) / TIME_CONVERSION_BASE);

    //! 转换为带有格式的时间戳
    auto timePoint = system_clock::time_point(sec + msec);
    auto timestamp = system_clock::to_time_t(timePoint);

    //! 转换为本地时间
    std::tm localTime{};
    localtime_r(&timestamp, &localTime);

    return LogCollector::format(
        "{}-{}-{L 2 0} {L 2 0}:{L 2 0}:{L 2 0}.{L 3 0}",
        localTime.tm_year + TIME_YEAR_OFFSET,
        localTime.tm_mon + TIME_MONTH_OFFSET,
        localTime.tm_mday,
        localTime.tm_hour,
        localTime.tm_min,
        localTime.tm_sec,
        msec.count()
    );
}

bool JournalLoggerHandler::registerJournalHandler()
{
    //! 打开jounal 对象
    if (int r = sd_journal_open(&mJournal, SD_JOURNAL_LOCAL_ONLY); r < 0) {
        SS_LOG(Severity::Level::ERROR,
            "readFromJournal -> Failed to open journal: %s", strerror(-r));
        return false;
    }

    //! 获取 journal 的文件描述符
    int fd = sd_journal_get_fd(mJournal);
    if (fd < 0) {
        SS_LOG(Severity::Level::ERROR,
            "readFromJournal -> Failed to get journal fd: %s", strerror(-fd));
        sd_journal_close(mJournal);
        return false;
    }

    initializeJournalLoggers();

    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    mStartTimestamp = static_cast<uint64_t>(ts.tv_sec) * TIME_CONVERSION_DOUBLE
        + ts.tv_nsec / TIME_CONVERSION_BASE;

    //! 定位到日志的末尾(类似于end(), 是末尾的虚拟位)
    sd_journal_seek_tail(mJournal);
    sd_journal_next(mJournal);

    //! 监听 journal 文件描述符的可读事件
    sd_event_add_io(getEvent(), nullptr, fd, EPOLLIN,
        [](sd_event_source *s, int fd, uint32_t revents, void *userdata) {
            JournalLoggerHandler* handler = static_cast<JournalLoggerHandler*>(userdata);
            handler->onJournaldReadable();
            return 1;
    }, this);

    //! 注册定时器（防止 missed wakeup）
    auto r = sd_event_add_time(getEvent(), &mTimer, CLOCK_MONOTONIC, 0, 0,
        [](sd_event_source *s, uint64_t usec, void *userdata) {
        JournalLoggerHandler* handler = static_cast<JournalLoggerHandler*>(userdata);
        handler->onJournaldReadable();
        uint64_t t;
        sd_journal_get_timeout(handler->mJournal, &t);
        sd_event_source_set_time(handler->mTimer, t);
        sd_event_source_set_enabled(handler->mTimer, SD_EVENT_ON);
        return 0;
    }, this);

    if (r < 0) {
        fprintf(stderr, "Failed to add timer: %s\n", strerror(-r));
        return false;
    }
    sd_event_source_set_enabled(mTimer, SD_EVENT_ON);

    SS_LOG(Severity::Level::DEBUG, "Successfully registerJournalHandler.");

    return true;
}

bool JournalLoggerHandler::initializeJournalLoggers()
{
    //! 获取config文件中的配置项
    auto configs = LogCollector::__loadLogConfigureAll();
    bool isFirst = true;
    for (const auto& config : configs) {
        if (!config.mFromLocal) {
            //! 订阅对应的进程名
            pid_t pid = LogCollector::__getProcessIdByName(config.mProcessName);
            int r = sd_journal_add_match(
                mJournal,
                ("_EXE=" + config.mExecPath).c_str(), 0);
            if (r < 0) {
                SS_LOG(Severity::Level::ERROR,
                    "readFromJournal -> Failed to add match for process %s: %s",
                    config.mProcessName.c_str(), strerror(-r));
                sd_journal_close(mJournal);
                return false;
            }
            if (!isFirst) {
                sd_journal_add_disjunction(mJournal);
            }
            isFirst = false;
            //! 创建 LogCllector
            auto logger = std::shared_ptr<LogCollector::Logger>(
                LogCollector::__createDefaultLogger(
                    config.mProcessName.c_str())
            );

            //! 将configure赋值给logger
            logger->mFromLocal = config.mFromLocal;
            logger->mExecPath = new char[LogCollector::SINGLE_BUFFER_SIZE];
            std::strncpy(logger->mExecPath, config.mExecPath.c_str(), LogCollector::SINGLE_BUFFER_SIZE - 1);
            logger->mMaxFileCount = config.mMaxFileCount;
            logger->mMaxFileSize = config.mMaxFileSize;
            logger->mMaxFlushSize = config.mMaxFlushSize;
            logger->mMaxFlushTimeOut = config.mMaxFlushTimeOut;

            //! 初始化日志文件
            if (__initLogFile(logger.get()) != ReturnStatus::SUCCESS) {
                SS_LOG(Severity::Level::ERROR,
                    "readFromJournal -> Failed to init logger for process %s",
                    config.mProcessName.c_str());
                continue;
            }

            //! 新增flush Timer
            addLoggerFlushTimer(logger.get());
            
            //! 获取start time
            auto startTime = LogCollector::__getProcessStartTime(pid);

            mClients[logger->mProcessName] = JournalClient(
                pid,
                startTime,
                logger
            );

            SS_LOG(Severity::Level::DEBUG,
                "readFromJournal -> Added match for process %s<%d><%d>",
                config.mProcessName.c_str(), pid, startTime);
        }
    }

    return true;
}

bool JournalLoggerHandler::addLoggerFlushTimer(LogCollector::Logger *aLogger)
{
    if (!aLogger) {
        return false;
    }

    if (aLogger->mMaxFlushTimeOut <= 0) {
        SS_LOG(Severity::Level::WARNING, "%s: timeout <=0, do not set flusher timer", __func__);
        return false;
    }

    SS_LOG(Severity::Level::DEBUG, "addLoggerFlushTimer: timeout=%d ms", aLogger->mMaxFlushTimeOut);
    auto ret = sd_event_add_time_relative(getEvent(), nullptr, CLOCK_MONOTONIC,
        aLogger->mMaxFlushTimeOut * LogCollector::TIME_CONVERSION_BASE, 0,
        [](sd_event_source *aSource, uint64_t aUsec, void *aUserdata) {
            Logger* logger = static_cast<Logger*>(aUserdata);
            if (logger->mCurrentBufferSize > 0) {
                LogCollector::__flushLog(logger, nullptr, 0);
            }

            if (logger->mMaxFlushTimeOut > 0) {
                sd_event_source_set_time_relative(aSource, LogCollector::TIME_CONVERSION_BASE * logger->mMaxFlushTimeOut);
                sd_event_source_set_enabled(aSource, SD_EVENT_ON); //! 重新启用
            }
            return 0;
        }, aLogger
    );

    return ret >= 0;
}

void JournalLoggerHandler::handleLogFromJournal()
{
    const void *data;
    size_t len;
    std::string value;

    //! 获取日志的 EXE 字段
    value = getJournalField(mJournal, "_EXE");
    if (value.empty()) {
        return;
    }

    //! 将EXE转换成processName
    std::string processName = value.substr(value.find_last_of("/") + 1);

    const auto iter = mClients.find(processName);
    if (iter == mClients.end()) {
        return;
    }

    //! 判断client是否重启
    auto newClient = handleLogwhenClientChanged(iter->second);
    if (newClient != std::nullopt) {
        iter->second = newClient.value();
    }

    //! 生成并保存日志到Local
    generateLocalLog(iter->second.mLogger, iter->second.mPid);
}

void JournalLoggerHandler::onJournaldReadable()
{
    if (!mJournal) {
        return;
    }

    //! 处理新的日志条目
    if (sd_journal_process(mJournal) != SD_JOURNAL_APPEND) {
        return;
    }

    if (!mFirstEventHandled) {
        int res = sd_journal_seek_realtime_usec(mJournal, mStartTimestamp);
        SS_LOG_DEBUG("first event: sd_journal_seek_realtime_usec(%llu) -> %d",
                        mStartTimestamp, res);
        mFirstEventHandled = true;
    }

    while (sd_journal_next(mJournal) > 0) {
        handleLogFromJournal();
    }
}

std::optional<JournalLoggerHandler::JournalClient>
    JournalLoggerHandler::handleLogwhenClientChanged(const JournalClient& aOldClient)
{

    //! 获取日志的 PID 字段
    auto value = getJournalField(mJournal, "_PID");
    if (value.empty()) {
        return std::nullopt;
    }

    //! 判断进程是否重启
    pid_t newPid = std::stoi(value); 
    if (newPid == aOldClient.mPid) {
        //! TODO: 目前考虑到性能问题，暂时不使用start time进行二次验证
        return std::nullopt;
    }

    auto newStartTime = LogCollector::__getProcessStartTime(newPid);
    if (LogCollector::__flushLog(aOldClient.mLogger.get(), nullptr, 0) != ReturnStatus::SUCCESS) {
        SS_LOG(Severity::Level::WARNING, "%s<%d> flush old log failed",
            aOldClient.mLogger->mProcessName, aOldClient.mPid);
    }

    auto filePath = LogCollector::format(
        LogCollector::LOG_FILE_BARE_FMT, aOldClient.mLogger->mProcessName);
    if (fs::exists(filePath) && fs::file_size(filePath) > 0) {
        if (LogCollector::__rotateLog(aOldClient.mLogger.get()) != ReturnStatus::SUCCESS) {
            SS_LOG(Severity::Level::WARNING, "%s<%d> rotate log file failed",
                aOldClient.mLogger->mProcessName, aOldClient.mPid);
        }
    }

    SS_LOG(Severity::Level::WARNING,
        "Process<%s> restart, oldPid<%d>, oldStartTime=<%d>, newPid<%d>, newStartTime=<%d>",
        aOldClient.mLogger->mProcessName, aOldClient.mPid, aOldClient.mStartTime, newPid, newStartTime);
    return std::make_optional<JournalClient>(newPid, newStartTime, aOldClient.mLogger);
}

void JournalLoggerHandler::generateLocalLog(
    std::shared_ptr<LogCollector::Logger> aLogger, pid_t aPid)
{
    //! 获取日志的 PRIORITY 字段
    auto levelStr = getJournalField(mJournal, "PRIORITY");
    if (levelStr.empty()) {
        return;
    }

    Severity::Level level = Severity::fromJournalLevel(std::stoi(levelStr));

    if (static_cast<int>(level) < aLogger->mLevel) {
        return;
    }

    //! 获取日志的 MESSAGE 字段
    const auto msg = getJournalField(mJournal, "MESSAGE");
    if (msg.empty()) {
        return;
    }

    //! 获取日志的 CODE_FILE 字段
    auto fileStr = getJournalField(mJournal, "CODE_FILE");
    if (fileStr.empty()) {
        fileStr = "UnknownFile";
    }

    //! 获取日志的 CODE_LINE 字段
    const auto lineStr = getJournalField(mJournal, "CODE_LINE");
    int lineNum = -1;
    if (!lineStr.empty()) {
        lineNum = std::atoi(lineStr.c_str());
    }

    //! 获取日志的 CODE_FUNC 字段
    auto funcStr = getJournalField(mJournal, "CODE_FUNC");
    if (funcStr.empty()) {
        funcStr = "UnknownFunc";
    }

    //! 获取日志的原始时间戳 _SOURCE_REALTIME_TIMESTAMP 字段
    auto timeStamp = getJournalField(mJournal, "_SOURCE_REALTIME_TIMESTAMP");
    if (timeStamp.empty()) {
        timeStamp = "UnknownTimeStamp";
    } else {
        timeStamp = convertRawTime(std::stoull(timeStamp));
    }

    LogCollector::__writeLogSync(aLogger.get(), level, aPid, timeStamp.c_str(),
        fileStr.c_str(), lineNum, funcStr.c_str(),
        "%s", msg.c_str());
}

bool JournalLoggerHandler::flushAll()
{
    for (const auto& client : mClients) {
        auto [_, __, logger] = client.second;
        if (LogCollector::__flushLog(logger.get(), nullptr, 0) != ReturnStatus::SUCCESS) {
            SS_LOG(Severity::Level::ERROR, "%s flush failed", logger->mProcessName);
            return false;
        }
    }

    SS_LOG(Severity::Level::DEBUG, "All processes have flushed");

    return true;
}

bool JournalLoggerHandler::clearAll()
{
    for (const auto& client : mClients) {
        auto [_, __, logger] = client.second;
        if (LogCollector::__clearLogFile(logger.get()) != ReturnStatus::SUCCESS) {
            SS_LOG(Severity::Level::ERROR, "%s clear failed", logger->mProcessName);
            return false;
        }
    }

    SS_LOG(Severity::Level::DEBUG, "All processes have cleared");

    return true;
}
